/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

// ====CHAINCODE EXECUTION SAMPLES (CLI) ==================

// ==== Invoke pigs ====
// peer chaincode invoke -C myc -n mycc -c '{"Args":["initPig","pig1","blue","35","tom"]}'
// peer chaincode invoke -C myc -n mycc -c '{"Args":["quarantinePig","pig1", "true"]}'
// peer chaincode invoke -C myc -n mycc -c '{"Args":["sendPigToSlaughterHouse","pig1"]}'
// peer chaincode invoke -C myc -n mycc -c '{"Args":["initPig","pig2","red","50","tom"]}'
// peer chaincode invoke -C myc -n mycc -c '{"Args":["initPig","pig3","blue","70","tom"]}'
// peer chaincode invoke -C myc -n mycc -c '{"Args":["transferPig","pig2","jerry"]}'
// peer chaincode invoke -C myc -n mycc -c '{"Args":["transferPigsBasedOnColor","blue","jerry"]}'
// peer chaincode invoke -C myc -n mycc -c '{"Args":["delete","pig1"]}'

// ==== Query pigs ====
// peer chaincode query -C myc -n mycc -c '{"Args":["readPig","pig1"]}'
// peer chaincode query -C myc -n mycc -c '{"Args":["getPigsByRange","pig1","pig3"]}'
// peer chaincode query -C myc -n mycc -c '{"Args":["getHistoryForPig","pig1"]}'

// Rich Query (Only supported if CouchDB is used as state database):
//   peer chaincode query -C myc -n mycc -c '{"Args":["queryPigsByOwner","tom"]}'
//   peer chaincode query -C myc -n mycc -c '{"Args":["queryPigs","{\"selector\":{\"owner\":\"tom\"}}"]}'

// INDEXES TO SUPPORT COUCHDB RICH QUERIES
//
// Indexes in CouchDB are required in order to make JSON queries efficient and are required for
// any JSON query with a sort. As of Hyperledger Fabric 1.1, indexes may be packaged alongside
// chaincode in a META-INF/statedb/couchdb/indexes directory. Each index must be defined in its own
// text file with extension *.json with the index definition formatted in JSON following the
// CouchDB index JSON syntax as documented at:
// http://docs.couchdb.org/en/2.1.1/api/database/find.html#db-index
//
// This pigs02 example chaincode demonstrates a packaged
// index which you can find in META-INF/statedb/couchdb/indexes/indexOwner.json.
// For deployment of chaincode to production environments, it is recommended
// to define any indexes alongside chaincode so that the chaincode and supporting indexes
// are deployed automatically as a unit, once the chaincode has been installed on a peer and
// instantiated on a channel. See Hyperledger Fabric documentation for more details.
//
// If you have access to the your peer's CouchDB state database in a development environment,
// you may want to iteratively test various indexes in support of your chaincode queries.  You
// can use the CouchDB Fauxton interface or a command line curl utility to create and update
// indexes. Then once you finalize an index, include the index definition alongside your
// chaincode in the META-INF/statedb/couchdb/indexes directory, for packaging and deployment
// to managed environments.
//
// In the examples below you can find index definitions that support pigs02
// chaincode queries, along with the syntax that you can use in development environments
// to create the indexes in the CouchDB Fauxton interface or a curl command line utility.
//

//Example hostname:port configurations to access CouchDB.
//
//To access CouchDB docker container from within another docker container or from vagrant environments:
// http://couchdb:5984/
//
//Inside couchdb docker container
// http://127.0.0.1:5984/

// Index for docType, owner.
// Note that docType and owner fields must be prefixed with the "data" wrapper
//
// Index definition for use with Fauxton interface
// {"index":{"fields":["data.docType","data.owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
//
// Example curl command line to define index in the CouchDB channel_chaincode database
// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[\"data.docType\",\"data.owner\"]},\"name\":\"indexOwner\",\"ddoc\":\"indexOwnerDoc\",\"type\":\"json\"}" http://hostname:port/myc_pigs/_index
//

// Index for docType, owner, size (descending order).
// Note that docType, owner and size fields must be prefixed with the "data" wrapper
//
// Index definition for use with Fauxton interface
// {"index":{"fields":[{"data.size":"desc"},{"data.docType":"desc"},{"data.owner":"desc"}]},"ddoc":"indexSizeSortDoc", "name":"indexSizeSortDesc","type":"json"}
//
// Example curl command line to define index in the CouchDB channel_chaincode database
// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"data.size\":\"desc\"},{\"data.docType\":\"desc\"},{\"data.owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc_pigs/_index

// Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
//   peer chaincode query -C myc -n pigs -c '{"Args":["queryPigs","{\"selector\":{\"docType\":\"pig\",\"owner\":\"tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'

// Rich Query with index design doc specified only (Only supported if CouchDB is used as state database):
//   peer chaincode query -C myc -n pigs -c '{"Args":["queryPigs","{\"selector\":{\"docType\":{\"$eq\":\"pig\"},\"owner\":{\"$eq\":\"tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}'

package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

type pig struct {
	ObjectType   string `json:"docType"` //docType is used to distinguish the various types of objects in state database
	Name         string `json:"name"`    //the fieldtags are needed to keep case from bouncing around
	Color        string `json:"color"`
	Size         int    `json:"size"`
	Owner        string `json:"owner"`
	Quanrantined string `json:"quanrantined"`
}

// ===================================================================================
// Main
// ===================================================================================
func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

// Init initializes chaincode
// ===========================
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	return shim.Success(nil)
}

// Invoke - Our entry point for Invocations
// ========================================
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	function, args := stub.GetFunctionAndParameters()
	fmt.Println("invoke is running " + function)

	// Handle different functions
	if function == "initPig" { //create a new pig
		return t.initPig(stub, args)
	} else if function == "transferPig" { //change owner of a specific pig
		return t.transferPig(stub, args)
	} else if function == "transferPigsBasedOnColor" { //transfer all pigs of a certain color
		return t.transferPigsBasedOnColor(stub, args)
	} else if function == "delete" { //delete a pig
		return t.delete(stub, args)
	} else if function == "readPig" { //read a pig
		return t.readPig(stub, args)
	} else if function == "queryPigsByOwner" { //find pigs for owner X using rich query
		return t.queryPigsByOwner(stub, args)
	} else if function == "queryPigs" { //find pigs based on an ad hoc rich query
		return t.queryPigs(stub, args)
	} else if function == "getHistoryForPig" { //get history of values for a pig
		return t.getHistoryForPig(stub, args)
	} else if function == "getPigsByRange" { //get pigs based on range query
		return t.getPigsByRange(stub, args)
	} else if function == "quarantinePig" { //quarantine pig
		return t.quarantinePig(stub, args)
	} else if function == "sendPigToSlaughterHouse" { //slaughter pig
		return t.sendPigToSlaughterHouse(stub, args)
	}

	fmt.Println("invoke did not find func: " + function) //error
	return shim.Error("Received unknown function invocation")
}

// ============================================================
// initPig - create a new pig, store into chaincode state
// ============================================================
func (t *SimpleChaincode) initPig(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var err error

	//   0       1       2     3
	// "asdf", "blue", "35", "bob"
	if len(args) != 4 {
		return shim.Error("Incorrect number of arguments. Expecting 4")
	}

	// ==== Input sanitation ====
	fmt.Println("- start init pig")
	if len(args[0]) <= 0 {
		return shim.Error("1st argument must be a non-empty string")
	}
	if len(args[1]) <= 0 {
		return shim.Error("2nd argument must be a non-empty string")
	}
	if len(args[2]) <= 0 {
		return shim.Error("3rd argument must be a non-empty string")
	}
	if len(args[3]) <= 0 {
		return shim.Error("4th argument must be a non-empty string")
	}
	pigName := args[0]
	color := strings.ToLower(args[1])
	owner := strings.ToLower(args[3])
	size, err := strconv.Atoi(args[2])
	if err != nil {
		return shim.Error("3rd argument must be a numeric string")
	}

	// ==== Check if pig already exists ====
	pigAsBytes, err := stub.GetState(pigName)
	if err != nil {
		return shim.Error("Failed to get pig: " + err.Error())
	} else if pigAsBytes != nil {
		fmt.Println("This pig already exists: " + pigName)
		return shim.Error("This pig already exists: " + pigName)
	}

	// ==== Create pig object and marshal to JSON ====
	objectType := "pig"
	pig := &pig{objectType, pigName, color, size, owner, "false"}
	pigJSONasBytes, err := json.Marshal(pig)
	if err != nil {
		return shim.Error(err.Error())
	}
	//Alternatively, build the pig json string manually if you don't want to use struct marshalling
	//pigJSONasString := `{"docType":"Pig",  "name": "` + pigName + `", "color": "` + color + `", "size": ` + strconv.Itoa(size) + `, "owner": "` + owner + `"}`
	//pigJSONasBytes := []byte(str)

	// === Save pig to state ===
	err = stub.PutState(pigName, pigJSONasBytes)
	if err != nil {
		return shim.Error(err.Error())
	}

	//  ==== Index the pig to enable color-based range queries, e.g. return all blue pigs ====
	//  An 'index' is a normal key/value entry in state.
	//  The key is a composite key, with the elements that you want to range query on listed first.
	//  In our case, the composite key is based on indexName~color~name.
	//  This will enable very efficient state range queries based on composite keys matching indexName~color~*
	indexName := "color~name"
	colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{pig.Color, pig.Name})
	if err != nil {
		return shim.Error(err.Error())
	}
	//  Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the pig.
	//  Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value
	value := []byte{0x00}
	stub.PutState(colorNameIndexKey, value)

	// ==== Pig saved and indexed. Return success ====
	fmt.Println("- end init pig")
	return shim.Success(nil)
}

// ===============================================
// readPig - read a pig from chaincode state
// ===============================================
func (t *SimpleChaincode) readPig(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var name, jsonResp string
	var err error

	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting name of the pig to query")
	}

	name = args[0]
	valAsbytes, err := stub.GetState(name) //get the pig from chaincode state
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"
		return shim.Error(jsonResp)
	} else if valAsbytes == nil {
		jsonResp = "{\"Error\":\"Pig does not exist: " + name + "\"}"
		return shim.Error(jsonResp)
	}

	return shim.Success(valAsbytes)
}

// ==================================================
// delete - remove a pig key/value pair from state
// ==================================================
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var jsonResp string
	var pigJSON pig
	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}
	pigName := args[0]

	// to maintain the color~name index, we need to read the pig first and get its color
	valAsbytes, err := stub.GetState(pigName) //get the pig from chaincode state
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to get state for " + pigName + "\"}"
		return shim.Error(jsonResp)
	} else if valAsbytes == nil {
		jsonResp = "{\"Error\":\"Pig does not exist: " + pigName + "\"}"
		return shim.Error(jsonResp)
	}

	err = json.Unmarshal([]byte(valAsbytes), &pigJSON)
	if err != nil {
		jsonResp = "{\"Error\":\"Failed to decode JSON of: " + pigName + "\"}"
		return shim.Error(jsonResp)
	}

	err = stub.DelState(pigName) //remove the pig from chaincode state
	if err != nil {
		return shim.Error("Failed to delete state:" + err.Error())
	}

	// maintain the index
	indexName := "color~name"
	colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{pigJSON.Color, pigJSON.Name})
	if err != nil {
		return shim.Error(err.Error())
	}

	//  Delete index entry to state.
	err = stub.DelState(colorNameIndexKey)
	if err != nil {
		return shim.Error("Failed to delete state:" + err.Error())
	}
	return shim.Success(nil)
}

// ===========================================================
// transfer a pig by setting a new owner name on the pig
// ===========================================================
func (t *SimpleChaincode) transferPig(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	//   0       1
	// "name", "bob"
	if len(args) < 2 {
		return shim.Error("Incorrect number of arguments. Expecting 2")
	}

	pigName := args[0]
	newOwner := strings.ToLower(args[1])
	fmt.Println("- start transferPig ", pigName, newOwner)

	pigAsBytes, err := stub.GetState(pigName)
	if err != nil {
		return shim.Error("Failed to get pig:" + err.Error())
	} else if pigAsBytes == nil {
		return shim.Error("Pig does not exist")
	}

	pigToTransfer := pig{}
	err = json.Unmarshal(pigAsBytes, &pigToTransfer) //unmarshal it aka JSON.parse()
	if err != nil {
		return shim.Error(err.Error())
	}
	pigToTransfer.Owner = newOwner //change the owner

	pigJSONasBytes, _ := json.Marshal(pigToTransfer)
	err = stub.PutState(pigName, pigJSONasBytes) //rewrite the pig
	if err != nil {
		return shim.Error(err.Error())
	}

	fmt.Println("- end transferPig (success)")
	return shim.Success(nil)
}

// ===========================================================================================
// getPigsByRange performs a range query based on the start and end keys provided.

// Read-only function results are not typically submitted to ordering. If the read-only
// results are submitted to ordering, or if the query is used in an update transaction
// and submitted to ordering, then the committing peers will re-execute to guarantee that
// result sets are stable between endorsement time and commit time. The transaction is
// invalidated by the committing peers if the result set has changed between endorsement
// time and commit time.
// Therefore, range queries are a safe option for performing update transactions based on query results.
// ===========================================================================================
func (t *SimpleChaincode) getPigsByRange(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	if len(args) < 2 {
		return shim.Error("Incorrect number of arguments. Expecting 2")
	}

	startKey := args[0]
	endKey := args[1]

	resultsIterator, err := stub.GetStateByRange(startKey, endKey)
	if err != nil {
		return shim.Error(err.Error())
	}
	defer resultsIterator.Close()

	// buffer is a JSON array containing QueryResults
	var buffer bytes.Buffer
	buffer.WriteString("[")

	bArrayMemberAlreadyWritten := false
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return shim.Error(err.Error())
		}
		// Add a comma before array members, suppress it for the first array member
		if bArrayMemberAlreadyWritten == true {
			buffer.WriteString(",")
		}
		buffer.WriteString("{\"Key\":")
		buffer.WriteString("\"")
		buffer.WriteString(queryResponse.Key)
		buffer.WriteString("\"")

		buffer.WriteString(", \"Record\":")
		// Record is a JSON object, so we write as-is
		buffer.WriteString(string(queryResponse.Value))
		buffer.WriteString("}")
		bArrayMemberAlreadyWritten = true
	}
	buffer.WriteString("]")

	fmt.Printf("- getPigsByRange queryResult:\n%s\n", buffer.String())

	return shim.Success(buffer.Bytes())
}

// ==== Example: GetStateByPartialCompositeKey/RangeQuery =========================================
// transferPigsBasedOnColor will transfer pigs of a given color to a certain new owner.
// Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'.
// Committing peers will re-execute range queries to guarantee that result sets are stable
// between endorsement time and commit time. The transaction is invalidated by the
// committing peers if the result set has changed between endorsement time and commit time.
// Therefore, range queries are a safe option for performing update transactions based on query results.
// ===========================================================================================
func (t *SimpleChaincode) transferPigsBasedOnColor(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	//   0       1
	// "color", "bob"
	if len(args) < 2 {
		return shim.Error("Incorrect number of arguments. Expecting 2")
	}

	color := args[0]
	newOwner := strings.ToLower(args[1])
	fmt.Println("- start transferPigsBasedOnColor ", color, newOwner)

	// Query the color~name index by color
	// This will execute a key range query on all keys starting with 'color'
	coloredPigResultsIterator, err := stub.GetStateByPartialCompositeKey("color~name", []string{color})
	if err != nil {
		return shim.Error(err.Error())
	}
	defer coloredPigResultsIterator.Close()

	// Iterate through result set and for each pig found, transfer to newOwner
	var i int
	for i = 0; coloredPigResultsIterator.HasNext(); i++ {
		// Note that we don't get the value (2nd return variable), we'll just get the pig name from the composite key
		responseRange, err := coloredPigResultsIterator.Next()
		if err != nil {
			return shim.Error(err.Error())
		}

		// get the color and name from color~name composite key
		objectType, compositeKeyParts, err := stub.SplitCompositeKey(responseRange.Key)
		if err != nil {
			return shim.Error(err.Error())
		}
		returnedColor := compositeKeyParts[0]
		returnedPigName := compositeKeyParts[1]
		fmt.Printf("- found a pig from index:%s color:%s name:%s\n", objectType, returnedColor, returnedPigName)

		// Now call the transfer function for the found pig.
		// Re-use the same function that is used to transfer individual pigs
		response := t.transferPig(stub, []string{returnedPigName, newOwner})
		// if the transfer failed break out of loop and return error
		if response.Status != shim.OK {
			return shim.Error("Transfer failed: " + response.Message)
		}
	}

	responsePayload := fmt.Sprintf("Transferred %d %s pigs to %s", i, color, newOwner)
	fmt.Println("- end transferPigsBasedOnColor: " + responsePayload)
	return shim.Success([]byte(responsePayload))
}

// =======Rich queries =========================================================================
// Two examples of rich queries are provided below (parameterized query and ad hoc query).
// Rich queries pass a query string to the state database.
// Rich queries are only supported by state database implementations
//  that support rich query (e.g. CouchDB).
// The query string is in the syntax of the underlying state database.
// With rich queries there is no guarantee that the result set hasn't changed between
//  endorsement time and commit time, aka 'phantom reads'.
// Therefore, rich queries should not be used in update transactions, unless the
// application handles the possibility of result set changes between endorsement and commit time.
// Rich queries can be used for point-in-time queries against a peer.
// ============================================================================================

// ===== Example: Parameterized rich query =================================================
// queryPigsByOwner queries for pigs based on a passed in owner.
// This is an example of a parameterized query where the query logic is baked into the chaincode,
// and accepting a single query parameter (owner).
// Only available on state databases that support rich query (e.g. CouchDB)
// =========================================================================================
func (t *SimpleChaincode) queryPigsByOwner(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	//   0
	// "bob"
	if len(args) < 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}

	owner := strings.ToLower(args[0])

	queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"pig\",\"owner\":\"%s\"}}", owner)

	queryResults, err := getQueryResultForQueryString(stub, queryString)
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(queryResults)
}

// ===== Example: Ad hoc rich query ========================================================
// queryPigs uses a query string to perform a query for pigs.
// Query string matching state database syntax is passed in and executed as is.
// Supports ad hoc queries that can be defined at runtime by the client.
// If this is not desired, follow the queryPigsForOwner example for parameterized queries.
// Only available on state databases that support rich query (e.g. CouchDB)
// =========================================================================================
func (t *SimpleChaincode) queryPigs(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	//   0
	// "queryString"
	if len(args) < 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}

	queryString := args[0]

	queryResults, err := getQueryResultForQueryString(stub, queryString)
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(queryResults)
}

// =========================================================================================
// getQueryResultForQueryString executes the passed in query string.
// Result set is built and returned as a byte array containing the JSON results.
// =========================================================================================
func getQueryResultForQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) {

	fmt.Printf("- getQueryResultForQueryString queryString:\n%s\n", queryString)

	resultsIterator, err := stub.GetQueryResult(queryString)
	if err != nil {
		return nil, err
	}
	defer resultsIterator.Close()

	// buffer is a JSON array containing QueryRecords
	var buffer bytes.Buffer
	buffer.WriteString("[")

	bArrayMemberAlreadyWritten := false
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return nil, err
		}
		// Add a comma before array members, suppress it for the first array member
		if bArrayMemberAlreadyWritten == true {
			buffer.WriteString(",")
		}
		buffer.WriteString("{\"Key\":")
		buffer.WriteString("\"")
		buffer.WriteString(queryResponse.Key)
		buffer.WriteString("\"")

		buffer.WriteString(", \"Record\":")
		// Record is a JSON object, so we write as-is
		buffer.WriteString(string(queryResponse.Value))
		buffer.WriteString("}")
		bArrayMemberAlreadyWritten = true
	}
	buffer.WriteString("]")

	fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String())

	return buffer.Bytes(), nil
}

func (t *SimpleChaincode) getHistoryForPig(stub shim.ChaincodeStubInterface, args []string) pb.Response {

	if len(args) < 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}

	pigName := args[0]

	fmt.Printf("- start getHistoryForPig: %s\n", pigName)

	resultsIterator, err := stub.GetHistoryForKey(pigName)
	if err != nil {
		return shim.Error(err.Error())
	}
	defer resultsIterator.Close()

	// buffer is a JSON array containing historic values for the pig
	var buffer bytes.Buffer
	buffer.WriteString("[")

	bArrayMemberAlreadyWritten := false
	for resultsIterator.HasNext() {
		response, err := resultsIterator.Next()
		if err != nil {
			return shim.Error(err.Error())
		}
		// Add a comma before array members, suppress it for the first array member
		if bArrayMemberAlreadyWritten == true {
			buffer.WriteString(",")
		}
		buffer.WriteString("{\"TxId\":")
		buffer.WriteString("\"")
		buffer.WriteString(response.TxId)
		buffer.WriteString("\"")

		buffer.WriteString(", \"Value\":")
		// if it was a delete operation on given key, then we need to set the
		//corresponding value null. Else, we will write the response.Value
		//as-is (as the Value itself a JSON pig)
		if response.IsDelete {
			buffer.WriteString("null")
		} else {
			buffer.WriteString(string(response.Value))
		}

		buffer.WriteString(", \"Timestamp\":")
		buffer.WriteString("\"")
		buffer.WriteString(time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)).String())
		buffer.WriteString("\"")

		buffer.WriteString(", \"IsDelete\":")
		buffer.WriteString("\"")
		buffer.WriteString(strconv.FormatBool(response.IsDelete))
		buffer.WriteString("\"")

		buffer.WriteString("}")
		bArrayMemberAlreadyWritten = true
	}
	buffer.WriteString("]")

	fmt.Printf("- getHistoryForPig returning:\n%s\n", buffer.String())

	return shim.Success(buffer.Bytes())
}

// =========================================================================================
// Get pig struct by pig name
// =========================================================================================
func (t *SimpleChaincode) getPigStruct(stub shim.ChaincodeStubInterface, pigName string) (pig, error) {

	pigStruct := pig{}

	pigAsBytes, err := stub.GetState(pigName)
	if err != nil {
		return pigStruct, errors.New("Failed to get pig:" + err.Error())
	} else if pigAsBytes == nil {
		return pigStruct, errors.New("Pig does not exist")
	}

	err = json.Unmarshal(pigAsBytes, &pigStruct) //unmarshal it aka JSON.parse()
	if err != nil {
		return pigStruct, err
	}

	return pigStruct, nil
}

// =========================================================================================
// Save pig struct by pig name
// =========================================================================================
func (t *SimpleChaincode) savePigStruct(stub shim.ChaincodeStubInterface, pigStruct pig) error {
	pigJSONasBytes, _ := json.Marshal(pigStruct)
	err := stub.PutState(pigStruct.Name, pigJSONasBytes) //rewrite the pig
	return err
}

func (t *SimpleChaincode) quarantinePig(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) < 2 {
		return shim.Error("Incorrect number of arguments. Expecting 2")
	}

	pigName := args[0]
	quarantined := args[1]

	fmt.Printf("- start quarantinePig: %s\n", pigName)

	pigToQuarantine, err := t.getPigStruct(stub, pigName)
	if err != nil {
		return shim.Error(err.Error())
	}

	pigToQuarantine.Quanrantined = quarantined

	err = t.savePigStruct(stub, pigToQuarantine)
	if err != nil {
		return shim.Error(err.Error())
	}

	fmt.Println("- end quarantinePig (success)")
	return shim.Success(nil)
}

func (t *SimpleChaincode) sendPigToSlaughterHouse(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) < 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}

	pigName := args[0]

	fmt.Printf("- start sendPigToSlaughterHouse: %s\n", pigName)

	pigStruct, err := t.getPigStruct(stub, pigName)
	if err != nil {
		return shim.Error(err.Error())
	}

	if pigStruct.Quanrantined != "true" {
		return shim.Error("Pig can not be sent to slaughter house before quanrantined: " + pigName)
	}

	pigStruct.Owner = "SlaughterHouse"

	err = t.savePigStruct(stub, pigStruct)
	if err != nil {
		return shim.Error(err.Error())
	}

	fmt.Println("- end sendPigToSlaughterHouse (success)")
	return shim.Success(nil)
}
